<

Web 上で最初の Flutter アプリを作成する

The web app that you'll be building

これは、最初の Flutter を作成するためのガイドですウェブアプリ。 オブジェクト指向プログラミングに精通している場合は、 変数、ループ、条件などの概念、 このチュートリアルを完了できます。 Dart の経験は必要ありません。 モバイルまたはウェブプログラミング。

構築するもの

サインイン画面を表示する単純な Web アプリを実装します。 画面には、名、名前、 姓とユーザー名。ユーザーがフィールドに入力すると、 進行状況バーがサインイン領域の上部に沿ってアニメーション表示されます。 3 つのフィールドすべてに入力すると、進行状況バーが表示されます。 サインインエリアの全幅に沿って緑色で表示されます。 そしてそのサインアップボタンが有効になります。 をクリックすると、サインアップボタンをクリックするとようこそ画面が表示されます 画面の下からアニメーション化します。

アニメーション GIF は、このラボの完了時にアプリがどのように動作するかを示しています。

ステップ 0: スターター Web アプリを入手する

私たちが提供するシンプルな Web アプリから始めます。

  1. Web 開発を有効にします。
    コマンドラインで次のコマンドを実行して、 Flutter が正しくインストールされていることを確認してください。

    $ flutter doctor
    Doctor summary (to see all details, run flutter doctor -v):
    [✓] Flutter (Channel master, 3.4.0-19.0.pre.254, on macOS 12.6 21G115
        darwin-arm64, locale en)
    [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 14.0)
    [✓] Chrome - develop for the web
    [✓] Android Studio (version 2021.2)
    [✓] VS Code (version 1.71.1)
    [✓] Connected device (4 available)
    [✓] HTTP Host Availability
    
    • No issues found!
    

    「flutter: コマンドが見つかりません」と表示された場合は、 次に、インストールされていることを確認してください flutterSDKそしてそれがあなたの行く手にあることを。

    Android ツールチェーンである Android Studio であれば問題ありません。 Xcode ツールがインストールされていない場合、 アプリはウェブのみを対象としているためです。 後でこのアプリをモバイルでも動作させたい場合は、 追加のインストールとセットアップを行う必要があります。

  2. デバイスをリストします。
    ウェブを確実にするにはインストールされている、 利用可能なデバイスをリストします。 次のようなものが表示されるはずです。

    $ flutter devices
    4 connected devices:
    
    sdk gphone64 arm64 (mobile) • emulator-5554                        •
    android-arm64  • Android 13 (API 33) (emulator)
    iPhone 14 Pro Max (mobile)  • 45A72BE1-2D4E-4202-9BB3-D6AE2601BEF8 • ios
    • com.apple.CoreSimulator.SimRuntime.iOS-16-0 (simulator)
    macOS (desktop)             • macos                                •
    darwin-arm64   • macOS 12.6 21G115 darwin-arm64
    Chrome (web)                • chrome                               •
    web-javascript • Google Chrome 105.0.5195.125
    
    

    クロムデバイスは自動的に Chrome を起動し、使用できるようにします Flutter DevTools ツールの。

  3. 以下のDartPadに起動アプリが表示されます。

    {$ begin main.dart $}
    import 'package:flutter/material.dart';
    
    void main() => runApp(const SignUpApp());
    
    class SignUpApp extends StatelessWidget {
      const SignUpApp();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: {
            '/': (context) => const SignUpScreen(),
          },
        );
      }
    }
    
    class SignUpScreen extends StatelessWidget {
      const SignUpScreen();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey[200],
          body: const Center(
            child: SizedBox(
              width: 400,
              child: Card(
                child: SignUpForm(),
              ),
            ),
          ),
        );
      }
    }
    
    class SignUpForm extends StatefulWidget {
      const SignUpForm();
    
      @override
      State<SignUpForm> createState() => _SignUpFormState();
    }
    
    class _SignUpFormState extends State<SignUpForm> {
      final _firstNameTextController = TextEditingController();
      final _lastNameTextController = TextEditingController();
      final _usernameTextController = TextEditingController();
    
      double _formProgress = 0;
    
      @override
      Widget build(BuildContext context) {
        return Form(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              LinearProgressIndicator(value: _formProgress),
              Text('Sign up', style: Theme.of(context).textTheme.headlineMedium),
              Padding(
                padding: const EdgeInsets.all(8),
                child: TextFormField(
                  controller: _firstNameTextController,
                  decoration: const InputDecoration(hintText: 'First name'),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8),
                child: TextFormField(
                  controller: _lastNameTextController,
                  decoration: const InputDecoration(hintText: 'Last name'),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8),
                child: TextFormField(
                  controller: _usernameTextController,
                  decoration: const InputDecoration(hintText: 'Username'),
                ),
              ),
              TextButton(
                style: ButtonStyle(
                  foregroundColor: MaterialStateProperty.resolveWith(
                      (Set<MaterialState> states) {
                    return states.contains(MaterialState.disabled)
                        ? null
                        : Colors.white;
                  }),
                  backgroundColor: MaterialStateProperty.resolveWith(
                      (Set<MaterialState> states) {
                    return states.contains(MaterialState.disabled)
                        ? null
                        : Colors.blue;
                  }),
                ),
                onPressed: null,
                child: const Text('Sign up'),
              ),
            ],
          ),
        );
      }
    }
    {$ end main.dart $}
    {$ begin test.dart $}
    // Avoid warning on "double _formProgress = 0;"
    //ignore_for_file: prefer_final_fields
    {$ end test.dart $}
  4. 例を実行します。
    クリック走るボタンをクリックして例を実行します。 テキストフィールドに入力できることに注意してください。 しかしサインアップボタンが無効になっています。

  5. コードをコピーします。
    右上にあるクリップボードアイコンをクリックします。 コードペインを使用して、Dart コードをクリップボードにコピーします。

  6. 新しい Flutter プロジェクトを作成します。
    IDE、エディター、またはコマンドラインから、新しい Flutter プロジェクトを作成するそしてそれに名前を付けますsignin_example

  7. の内容を置き換えますlib/main.dartクリップボードの内容とともに。

観察

  • この例のコード全体は、lib/main.dartファイル。
  • Java を知っている人であれば、Dart 言語は非常に親しみのあるものに感じられるはずです。
  • アプリの UI はすべて Dart コードで作成されます。 詳細については、を参照してください。宣言型 UI の概要
  • アプリのUIは以下に準拠していますマテリアルデザイン、 あらゆるデバイスやプラットフォームで動作するビジュアル デザイン言語。 マテリアル デザイン ウィジェットをカスタマイズできます。 でも、他のものが好みなら、 Flutter は、Cupertino ウィジェット ライブラリも提供しています。 現在の iOS デザイン言語を実装します。 または、独自のカスタム ウィジェット ライブラリを作成することもできます。
  • Flutter では、ほとんどすべてがウィジェット。 アプリ自体もウィジェットです。 アプリの UI はウィジェット ツリーとして説明できます。

ステップ 1: ようこそ画面を表示する

SignUpFormクラスはステートフルなウィジェットです。 これは単にウィジェットに情報が保存されることを意味します。 ユーザー入力やフィードからのデータなど、変更される可能性のあるもの。 ウィジェット自体は不変なので (作成後に変更することはできません)、 Flutter は状態情報をコンパニオンクラスに保存します。 と呼ばれるStateクラス。この研究室では、 すべての編集は非公開に対して行われます_SignUpFormStateクラス。

まず、あなたの中でlib/main.dartファイル、 次のクラス定義を追加します。WelcomeScreen後のウィジェットSignUpScreenクラス:

class WelcomeScreen extends StatelessWidget {
  const WelcomeScreen();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child:
            Text('Welcome!', style: Theme.of(context).textTheme.displayMedium),
      ),
    );
  }
}

次に、画面を表示するボタンを有効にします。 そしてそれを表示するメソッドを作成します。

  1. を見つけます。build()の方法_SignUpFormStateクラス。これはコードの一部です これで「SignUp」ボタンが構築されます。 ボタンがどのように定義されているかに注目してください。 それはTextButton青色の背景で、 という白い文字サインアップそして、押すと、 何もしません。

  2. を更新しますonPressed財産。
    変更onPressed(存在しない) を呼び出すプロパティ ようこそ画面を表示するメソッド。

    変化onPressed: null以下に:

    onPressed: _showWelcomeScreen,
  3. を追加します。_showWelcomeScreen方法。
    アナライザーによって報告されたエラーを修正します。_showWelcomeScreen定義されていません。の真上build()方法、 次の関数を追加します。

    void _showWelcomeScreen() {
      Navigator.of(context).pushNamed('/welcome');
    }
  4. を追加します。/welcomeルート。
    新しい画面を表示するための接続を作成します。 の中にbuild()の方法SignUpApp、 以下のルートを追加します'/':

    '/welcome': (context) => const WelcomeScreen(),
  5. アプリを実行します。
    サインアップボタンが有効になるはずです。 それをクリックすると、ようこそ画面が表示されます。 下からどのようにアニメーション化されるかに注目してください。 その動作は無料で行えます。

観察

  • _showWelcomeScreen()関数はで使用されますbuild()メソッドをコールバック関数として使用します。コールバック関数は多くの場合、 Dart コードで使用され、この場合、これは意味します 「ボタンが押されたときにこのメソッドを呼び出す」。
  • constコンストラクターの前のキーワードは非常に重要です 重要。 Flutter が定数ウィジェットに遭遇すると、 内部での再構築作業のほとんどをショートさせてしまう レンダリングをより効率的にします。
  • Flutter には 1 つだけありますNavigator物体。 Flutterの画面を管理するウィジェットです (とも呼ばれているルートまたページ) スタック内。 スタックの一番上の画面は、 現在は が表示されています。新しい画面をこれにプッシュします スタックは表示をその新しい画面に切り替えます。 このため、_showWelcomeScreen関数プッシュ のWelcomeScreenナビゲーターのスタックに追加します。 ユーザーがボタンをクリックすると、出来上がりです。 ようこそ画面が表示されます。同じく、 電話をかけるpop()Navigatorに戻ります 前の画面。 Flutterのナビゲーションは ブラウザのナビゲーションに統合されており、 これはブラウザの 戻る矢印ボタン。

ステップ 2: サインイン進行状況の追跡を有効にする

このサインイン画面には 3 つのフィールドがあります。 次に、追跡する機能を有効にします。 ユーザーのフォームフィールドへの入力の進捗状況、 フォームが完了したら、アプリの UI を更新します。

  1. 更新するメソッドを追加する_formProgress。 の中に_SignUpFormStateクラスに、という新しいメソッドを追加します。_updateFormProgress():

    void _updateFormProgress() {
      var progress = 0.0;
      final controllers = [
        _firstNameTextController,
        _lastNameTextController,
        _usernameTextController
      ];
    
      for (final controller in controllers) {
        if (controller.value.text.isNotEmpty) {
          progress += 1 / controllers.length;
        }
      }
    
      setState(() {
        _formProgress = progress;
      });
    }

    このメソッドは、_formProgressに基づくフィールド 空ではないテキストフィールドの数。

  2. 電話_updateFormProgressフォームのとき 変化します。
    の中にbuild()の方法_SignUpFormStateクラス、 にコールバックを追加しますFormウィジェットのonChanged口論。 NEW としてマークされた以下のコードを追加します。

    return Form(
      onChanged: _updateFormProgress, // NEW
      child: Column(
  3. を更新しますonPressed財産(再び)。
    step 1を変更しました。onPressedの財産サインアップボタンをクリックすると、ようこそ画面が表示されます。 次に、そのボタンを更新してようこそを表示します フォームが完全に入力された場合にのみ画面が表示されます。

    TextButton(
      style: ButtonStyle(
        foregroundColor: MaterialStateProperty.resolveWith(
            (Set<MaterialState> states) {
          return states.contains(MaterialState.disabled)
              ? null
              : Colors.white;
        }),
        backgroundColor: MaterialStateProperty.resolveWith(
            (Set<MaterialState> states) {
          return states.contains(MaterialState.disabled)
              ? null
              : Colors.blue;
        }),
      ),
      onPressed:
          _formProgress == 1 ? _showWelcomeScreen : null, // UPDATED
      child: const Text('Sign up'),
    ),
  4. アプリを実行します。
    サインアップボタンは最初は無効になっていますが、 ただし、3 つのテキストフィールドすべてに次の内容が含まれる場合に有効になります。 (任意の) テキスト。

観察

  • ウィジェットの呼び出しsetState()メソッドは Flutter に次のように伝えます。 ウィジェットは画面上で更新する必要があります。 その後、フレームワークは以前の不変ウィジェットを破棄します。 (およびその子)、新しいものを作成します (付随する子ウィジェットツリーを含む)、 そしてそれを画面にレンダリングします。これをシームレスに機能させるには、 flutterは高速である必要があります。 新しいウィジェット ツリーを作成して画面にレンダリングする必要があります 1/60 秒未満で滑らかなビジュアルを作成します トランジション、特にアニメーションの場合。 ラッキー flutter速い。
  • progressフィールドは浮動小数点値として定義されており、 で更新されます_updateFormProgress方法。 3 つのフィールドをすべて入力すると、_formProgressは 1.0 に設定されます。 いつ_formProgressが 1.0 に設定されている場合、onPressedコールバックはに設定されます_showWelcomeScreen方法。さて、それはonPressed引数が null 以外の場合、ボタンは有効になります。 Flutter のほとんどのマテリアル デザイン ボタンと同様、テキストボタンはデフォルトで無効になっています。onPressedonLongPressコールバックは null です。
  • 注目してください。_updateFormProgressに関数を渡しますsetState()。 これを匿名といいます 関数であり、次の構文があります。

    methodName(() {...});
    

    どこmethodName匿名を受け取る名前付き関数です。 コールバック関数を引数として指定します。

  • を表示する最後のステップの Dart 構文 ようこそ画面は次のとおりです。

    _formProgress == 1 ? _showWelcomeScreen : null

    これは Dart の条件付き代入であり、構文は次のとおりです。condition ? expression1 : expression2。 式の場合_formProgress == 1true の場合、式全体の結果は の左側の値に:、それは_showWelcomeScreenこの場合の方法。

ステップ 2.5: Dart DevTools を起動する

Flutter Web アプリをデバッグするにはどうすればよいですか? これは、Flutter アプリのデバッグとあまり変わりません。 使いたいDart 開発ツール! (Chrome DevTools と混同しないでください。)

私たちのアプリには現時点ではバグはありませんが、とにかくチェックしてみましょう。 DevTools を起動するための次の手順は、どのワークフローにも当てはまります。 ただし、IntelliJ を使用している場合はショートカットがあります。 詳細については、このセクションの最後にあるヒントを参照してください。

  1. アプリを実行します。
    アプリが現在実行されていない場合は、起動します。 を選択クロムプルダウンからのデバイス IDE から起動するか、 コマンドラインから、使用しますflutter run -d chrome

  2. DevTools の Web ソケット情報を取得します。
    コマンドラインまたはIDEで、 次のようなメッセージが表示されるはずです。

    Launching lib/main.dart on Chrome in debug mode...
    Building application for the web...                                11.7s
    Attempting to connect to browser instance..
    Debug service listening on ws://127.0.0.1:54998/pJqWWxNv92s=
    

    太字で示されているデバッグ サービスのアドレスをコピーします。 DevTools を起動するにはこれが必要になります。

  3. DevTools がインストールされていることを確認します。
    ありますか開発ツールがインストールされている? IDE を使用している場合は、 Flutter プラグインと Dart プラグインが設定されていることを確認してください。 で説明されているように、VSコードAndroid Studio と IntelliJページ。 コマンドラインで作業している場合は、 の説明に従って DevTools サーバーを起動します。DevTools コマンドラインページ。

  4. 開発ツールに接続します。
    DevTools が起動すると、何かが表示されるはずです 次のような:

    Serving DevTools at http://127.0.0.1:9100
    

    Chrome ブラウザでこの URL にアクセスします。 DevTools が表示されるはずです 起動画面。次のようになります。

    Screenshot of the DevTools launch screen

  5. 実行中のアプリに接続します。
    実行中のサイトに接続する、 手順 2 でコピーした ws の場所を貼り付けます。 をクリックし、「接続」をクリックします。 Dart DevTools が表示されるはずです。 Chrome ブラウザで正常に実行されています:

    Screenshot of DevTools running screen

    おめでとうございます。Dart DevTools を実行できるようになりました。

  1. ブレークポイントを設定します。
    これで DevTools が実行されました。 を選択デバッガ上部にある青いバーのタブをクリックします。 デバッガー ペインが表示され、左下に この例で使用されているライブラリのリストを参照してください。 選択するlib/main.dartDartコードを表示するには 中央のペインにあります。

    Screenshot of the DevTools debugger

  2. ブレークポイントを設定します。
    ダーツコードでは、 どこまで下にスクロールしますprogress更新されます:

    for (final controller in controllers) {
      if (controller.value.text.isNotEmpty) {
        progress += 1 / controllers.length;
      }
    }

    をクリックして、for ループのある行にブレークポイントを配置します。 行番号の左側。ブレークポイントが表示されます の中にブレークポイントウィンドウの左側のセクション。

  3. ブレークポイントをトリガーします。
    実行中のアプリで、テキスト フィールドの 1 つをクリックしてフォーカスを取得します。 アプリがブレークポイントに到達し、一時停止します。 DevTools 画面の左側に表示されます。 の値progress、これは 0 です。これは予想されることですが、 どのフィールドも入力されていないためです。 for ループを実行して確認します プログラムの実行。

  4. アプリを再開します。
    緑色のアイコンをクリックしてアプリを再開します履歴書DevTools ウィンドウのボタン。

  5. ブレークポイントを削除します。
    もう一度クリックしてブレークポイントを削除し、アプリを再開します。

これにより、DevTools を使用して何が可能になるかを少し垣間見ることができます。 他にもたくさんあります!詳細については、 を参照してください開発ツールのドキュメント

ステップ 3: サインイン中のアニメーションを追加する

アニメーションを追加する時が来ました!この最後のステップでは、 のアニメーションを作成しますLinearProgressIndicatorサインインの上部にある エリア。アニメーションは次のような動作をします。

  • アプリが起動すると、 サインイン領域の上部に小さな赤いバーが表示されます。
  • 1 つのテキスト フィールドにテキストが含まれている場合、 赤いバーがオレンジ色に変わり、0.15 がアニメーション化されます。 サインインエリアを横切る途中。
  • 2 つのテキスト フィールドにテキストが含まれている場合、 オレンジ色のバーが黄色に変わり、半分がアニメーションします サインインエリアを横切る途中。
  • 3 つのテキスト フィールドすべてにテキストが含まれている場合、 オレンジ色のバーが緑色に変わり、すべての要素がアニメーション化されます。 サインインエリアを越えて。 また、サインアップボタンが有効になります。
  1. を追加AnimatedProgressIndicator
    ファイルの最後に、次のウィジェットを追加します。

    class AnimatedProgressIndicator extends StatefulWidget {
      final double value;
    
      const AnimatedProgressIndicator({
        required this.value,
      });
    
      @override
      State<AnimatedProgressIndicator> createState() {
        return _AnimatedProgressIndicatorState();
      }
    }
    
    class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
      late Animation<Color?> _colorAnimation;
      late Animation<double> _curveAnimation;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
            duration: Duration(milliseconds: 1200), vsync: this);
    
        final colorTween = TweenSequence([
          TweenSequenceItem(
            tween: ColorTween(begin: Colors.red, end: Colors.orange),
            weight: 1,
          ),
          TweenSequenceItem(
            tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
            weight: 1,
          ),
          TweenSequenceItem(
            tween: ColorTween(begin: Colors.yellow, end: Colors.green),
            weight: 1,
          ),
        ]);
    
        _colorAnimation = _controller.drive(colorTween);
        _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
      }
    
      @override
      void didUpdateWidget(oldWidget) {
        super.didUpdateWidget(oldWidget);
        _controller.animateTo(widget.value);
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: _controller,
          builder: (context, child) => LinearProgressIndicator(
            value: _curveAnimation.value,
            valueColor: _colorAnimation,
            backgroundColor: _colorAnimation.value?.withOpacity(0.4),
          ),
        );
      }
    }

    didUpdateWidget機能アップデート のAnimatedProgressIndicatorStateいつでもAnimatedProgressIndicator変化します。

  2. 新しいものを使用してくださいAnimatedProgressIndicator
    次に、LinearProgressIndicatorの中にFormこの新しいものでAnimatedProgressIndicator:

    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        AnimatedProgressIndicator(value: _formProgress), // NEW
        Text('Sign up', style: Theme.of(context).textTheme.headlineMedium),
        Padding(

    このウィジェットはAnimatedBuilderアニメーション化する 最新値への進行状況インジケーター。

  3. アプリを実行します。
    3 つのフィールドに何かを入力して確認します。 アニメーションが動作し、サインアップボタンが表示されますいらっしゃいませ画面。

完全なサンプル

import 'package:flutter/material.dart';

void main() => runApp(const SignUpApp());

class SignUpApp extends StatelessWidget {
  const SignUpApp();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        '/': (context) => const SignUpScreen(),
        '/welcome': (context) => const WelcomeScreen(),
      },
    );
  }
}

class SignUpScreen extends StatelessWidget {
  const SignUpScreen();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: Center(
        child: SizedBox(
          width: 400,
          child: Card(
            child: SignUpForm(),
          ),
        ),
      ),
    );
  }
}

class WelcomeScreen extends StatelessWidget {
  const WelcomeScreen();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child:
            Text('Welcome!', style: Theme.of(context).textTheme.displayMedium),
      ),
    );
  }
}

class SignUpForm extends StatefulWidget {
  @override
  State<SignUpForm> createState() => _SignUpFormState();
}

class _SignUpFormState extends State<SignUpForm> {
  final _firstNameTextController = TextEditingController();
  final _lastNameTextController = TextEditingController();
  final _usernameTextController = TextEditingController();

  double _formProgress = 0;

  void _updateFormProgress() {
    var progress = 0.0;
    final controllers = [
      _firstNameTextController,
      _lastNameTextController,
      _usernameTextController
    ];

    for (final controller in controllers) {
      if (controller.value.text.isNotEmpty) {
        progress += 1 / controllers.length;
      }
    }

    setState(() {
      _formProgress = progress;
    });
  }

  void _showWelcomeScreen() {
    Navigator.of(context).pushNamed('/welcome');
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      onChanged: _updateFormProgress,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          AnimatedProgressIndicator(value: _formProgress),
          Text('Sign up', style: Theme.of(context).textTheme.headlineMedium),
          Padding(
            padding: const EdgeInsets.all(8),
            child: TextFormField(
              controller: _firstNameTextController,
              decoration: const InputDecoration(hintText: 'First name'),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8),
            child: TextFormField(
              controller: _lastNameTextController,
              decoration: const InputDecoration(hintText: 'Last name'),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8),
            child: TextFormField(
              controller: _usernameTextController,
              decoration: const InputDecoration(hintText: 'Username'),
            ),
          ),
          TextButton(
            style: ButtonStyle(
              foregroundColor: MaterialStateProperty.resolveWith(
                  (Set<MaterialState> states) {
                return states.contains(MaterialState.disabled)
                    ? null
                    : Colors.white;
              }),
              backgroundColor: MaterialStateProperty.resolveWith(
                  (Set<MaterialState> states) {
                return states.contains(MaterialState.disabled)
                    ? null
                    : Colors.blue;
              }),
            ),
            onPressed: _formProgress == 1 ? _showWelcomeScreen : null,
            child: const Text('Sign up'),
          ),
        ],
      ),
    );
  }
}

class AnimatedProgressIndicator extends StatefulWidget {
  final double value;

  const AnimatedProgressIndicator({
    required this.value,
  });

  @override
  State<AnimatedProgressIndicator> createState() {
    return _AnimatedProgressIndicatorState();
  }
}

class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Color?> _colorAnimation;
  late Animation<double> _curveAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 1200),
      vsync: this,
    );

    final colorTween = TweenSequence([
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.red, end: Colors.orange),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.yellow, end: Colors.green),
        weight: 1,
      ),
    ]);

    _colorAnimation = _controller.drive(colorTween);
    _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
  }

  @override
  void didUpdateWidget(oldWidget) {
    super.didUpdateWidget(oldWidget);
    _controller.animateTo(widget.value);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) => LinearProgressIndicator(
        value: _curveAnimation.value,
        valueColor: _colorAnimation,
        backgroundColor: _colorAnimation.value?.withOpacity(0.4),
      ),
    );
  }
}

観察

  • 使用できますAnimationController任意のアニメーションを実行します。
  • AnimatedBuilder値が次の場合にウィジェット ツリーを再構築します。 のAnimation変化します。
  • を使ってTween、ほぼすべての値の間を補間できます。 この場合、Color

次は何?

おめでとう! Flutter を使用して最初の Web アプリを作成しました。

このサンプルを引き続き使用したい場合は、 おそらくフォーム検証を追加できるでしょう。 これを行う方法についてのアドバイスについては、 を参照してください検証を伴うフォームの構築のレシピ flutterクックブック

Flutter Web アプリの詳細については、 Dart DevTools、または Flutter アニメーションについては、以下を参照してください。